// phalanx math_functions.hpp.cu header file

// Copyright (c) 2011 - 2011 Kohei Takahashi (Flast).
// Distributed under the MIT license. for more detail see COPYING.

// Function name with _fm suffix is forced using Fast-Math version.

#ifndef IG_PHALANX_MATH_FUNCTIONS_MATH_FUNCTIONS_HPP_CU_ONCE_
#define IG_PHALANX_MATH_FUNCTIONS_MATH_FUNCTIONS_HPP_CU_ONCE_

#include <phalanx/config.hpp.cu>

#include <cmath>

#include <boost/preprocessor/comma_if.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/arithmetic/dec.hpp>
#include <boost/preprocessor/comparison/not_equal.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>

#include <math_functions.h>

#include <phalanx/detail/define_func.hpp.cu>

#define EXPANDED_DECL( unused_z_, count_, data_ ) \
  BOOST_PP_TUPLE_ELEM( 3, 0, data_ )( \
    BOOST_PP_TUPLE_ELEM( 3, 1, data_ ).BOOST_PP_TUPLE_ELEM( 4, count_, ( x, y, z, w ) ) ) \
    BOOST_PP_COMMA_IF( \
      BOOST_PP_NOT_EQUAL( \
        count_, \
        BOOST_PP_DEC( BOOST_PP_TUPLE_ELEM( 3, 2, data_ ) ) ) )

#define EXPAND_( count_, function_, value_ ) \
  BOOST_PP_REPEAT( \
    count_, \
    EXPANDED_DECL, \
    ( function_, value_, count_ ) )

#define VECTOR_TYPE_OVERLOADS_DEF_( unused_z_, count_, data_ ) \
  PHALANX_CONFIG_DEFAULT_FUNCTION( BOOST_PP_TUPLE_ELEM( 3, 0, data_ ) ) \
  BOOST_PP_CAT( BOOST_PP_TUPLE_ELEM( 3, 1, data_ ), BOOST_PP_INC( count_ ) ) \
  BOOST_PP_TUPLE_ELEM( 3, 2, data_ )( \
    const BOOST_PP_CAT( BOOST_PP_TUPLE_ELEM( 3, 1, data_ ), BOOST_PP_INC( count_ ) ) &_x ) \
  { \
      BOOST_PP_CAT( BOOST_PP_TUPLE_ELEM( 3, 1, data_ ), BOOST_PP_INC( count_ ) ) _res = \
      { \
        EXPAND_( BOOST_PP_INC( count_ ), BOOST_PP_TUPLE_ELEM( 3, 2, data_ ), _x ) \
      }; \
      return _res; \
  }

#define VECTOR_TYPE_OVERLOADS( hostable_, max_count_, type_prefix_, function_ ) \
  BOOST_PP_REPEAT( \
    max_count_, \
    VECTOR_TYPE_OVERLOADS_DEF_, \
    ( hostable_, type_prefix_, function_ ) )

namespace phalanx
{

namespace math
{

// Reverce Square root
LO_PHALANX_PP_GENERIC( false )(
  rsqrt, 1,
  (( float, ( float ), ::rsqrtf ))
  (( double, ( double ), ::rsqrt )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, rsqrt );
VECTOR_TYPE_OVERLOADS( false, 4, double, rsqrt );

// Square root
LO_PHALANX_PP_GENERIC( true )(
  sqrt, 1,
  (( float, ( float ), ::sqrtf ))
  (( double, ( double ), ::sqrt )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, sqrt );
VECTOR_TYPE_OVERLOADS( true, 4, double, sqrt );

//
LO_PHALANX_PP_GENERIC( false )(
  cbrt, 1,
  (( float, ( float ), ::cbrtf ))
  (( double, ( double ), ::cbrt )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, cbrt );
VECTOR_TYPE_OVERLOADS( false, 4, double, cbrt );

//
LO_PHALANX_PP_GENERIC( false )(
  rcbrt, 1,
  (( float, ( float ), ::rcbrtf ))
  (( double, ( double ), ::rcbrt )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, rcbrt );
VECTOR_TYPE_OVERLOADS( false, 4, double, rcbrt );

//
LO_PHALANX_PP_GENERIC( false )(
  hypot, 2,
  (( float, ( float, float ), ::hypotf ))
  (( double, ( double, double ), ::hypot )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, hypot );
//VECTOR_TYPE_OVERLOADS( false, 4, double, hypot );

//
LO_PHALANX_PP_GENERIC( true )(
  exp, 1,
  (( float, ( float ), ::expf ))
  (( double, ( double ), ::exp )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, exp );
VECTOR_TYPE_OVERLOADS( true, 4, double, exp );

//
LO_PHALANX_PP_GENERIC( false )(
  exp_fm, 1,
  (( float, ( float ), ::__expf )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, exp_fm );

//
LO_PHALANX_PP_GENERIC( true )(
  exp2, 1,
  (( float, ( float ), ::exp2f ))
  (( double, ( double ), ::exp2 )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, exp2 );
VECTOR_TYPE_OVERLOADS( true, 4, double, exp2 );

//
LO_PHALANX_PP_GENERIC( true )(
  exp10, 1,
  (( float, ( float ), ::exp10f ))
  (( double, ( double ), ::exp10 )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, exp10 );
VECTOR_TYPE_OVERLOADS( true, 4, double, exp10 );

//
LO_PHALANX_PP_GENERIC( false )(
  exp10_fm, 1,
  (( float, ( float ), ::__exp10f )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, exp10_fm );

//
LO_PHALANX_PP_GENERIC( false )(
  expm1, 1,
  (( float, ( float ), ::expm1f ))
  (( double, ( double ), ::expm1 )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, expm1 );
VECTOR_TYPE_OVERLOADS( false, 4, double, expm1 );

//
LO_PHALANX_PP_GENERIC( true )(
  log, 1,
  (( float, ( float ), ::logf ))
  (( double, ( double ), ::log )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, log );
VECTOR_TYPE_OVERLOADS( true, 4, double, log );

//
LO_PHALANX_PP_GENERIC( false )(
  log_fm, 1,
  (( float, ( float ), ::__logf )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, log_fm );

//
LO_PHALANX_PP_GENERIC( true )(
  log2, 1,
  (( float, ( float ), ::log2f ))
  (( double, ( double ), ::log2 )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, log2 );
VECTOR_TYPE_OVERLOADS( true, 4, double, log2 );

//
LO_PHALANX_PP_GENERIC( false )(
  log2_fm, 1,
  (( float, ( float ), ::__log2f )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, log2_fm );

//
LO_PHALANX_PP_GENERIC( true )(
  log10, 1,
  (( float, ( float ), ::log10f ))
  (( double, ( double ), ::log10 )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, log10 );
VECTOR_TYPE_OVERLOADS( true, 4, double, log10 );

//
LO_PHALANX_PP_GENERIC( false )(
  log10_fm, 1,
  (( float, ( float ), ::__log10f )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, log10_fm );

//
LO_PHALANX_PP_GENERIC( false )(
  log1p, 1,
  (( float, ( float ), ::log1pf ))
  (( double, ( double ), ::log1p )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, log1p );
VECTOR_TYPE_OVERLOADS( false, 4, double, log1p );

// Sine
LO_PHALANX_PP_GENERIC( true )(
  sin, 1,
  (( float, ( float ), ::sinf ))
  (( double, ( double ), ::sin )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, sin );
VECTOR_TYPE_OVERLOADS( true, 4, double, sin );

LO_PHALANX_PP_GENERIC( false )(
  sin_fm, 1,
  (( float, ( float ), ::__sinf )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, sin_fm );

// Co-Sine
LO_PHALANX_PP_GENERIC( true )(
  cos, 1,
  (( float, ( float ), ::cosf ))
  (( double, ( double ), ::cos )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, cos );
VECTOR_TYPE_OVERLOADS( true, 4, double, cos );

LO_PHALANX_PP_GENERIC( false )(
  cos_fm, 1,
  (( float, ( float ), ::__cosf )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, cos_fm );

// Tangent
LO_PHALANX_PP_GENERIC( true )(
  tan, 1,
  (( float, ( float ), ::tanf ))
  (( double, ( double ), ::tan )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, tan );
VECTOR_TYPE_OVERLOADS( true, 4, double, tan );

LO_PHALANX_PP_GENERIC( false )(
  tan_fm, 1,
  (( float, ( float ), ::__tanf )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, tan_fm );

// Sine and Co-Sine
LO_PHALANX_PP_GENERIC( false )(
  sincos, 3,
  (( void, ( float, float *, float * ), ::sincosf ))
  (( void, ( double, double *, double * ), ::sincos )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, sincos );
//VECTOR_TYPE_OVERLOADS( false, 4, double, sincos );

LO_PHALANX_PP_GENERIC( false )(
  sincos_fm, 3,
  (( void, ( float, float *, float * ), ::__sincosf )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, sincos_fm );

//
LO_PHALANX_PP_GENERIC( false )(
  sinpi, 1,
  (( float, ( float ), ::sinpif ))
  (( double, ( double ), ::sinpi )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, sinpi );
VECTOR_TYPE_OVERLOADS( false, 4, double, sinpi );

// Arc-Sine
LO_PHALANX_PP_GENERIC( true )(
  asin, 1,
  (( float, ( float ), ::asinf ))
  (( double, ( double ), ::asin )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, asin );
VECTOR_TYPE_OVERLOADS( true, 4, double, asin );

// Arc-Co-Sine
LO_PHALANX_PP_GENERIC( true )(
  acos, 1,
  (( float, ( float ), ::acosf ))
  (( double, ( double ), ::acos )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, acos );
VECTOR_TYPE_OVERLOADS( true, 4, double, acos );

// Arc-Tangent
LO_PHALANX_PP_GENERIC( true )(
  atan, 1,
  (( float, ( float ), ::atanf ))
  (( double, ( double ), ::atan )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, atan );
VECTOR_TYPE_OVERLOADS( true, 4, double, atan );

//
LO_PHALANX_PP_GENERIC( true )(
  atan2, 2,
  (( float, ( float, float ), ::atan2f ))
  (( double, ( double, double ), ::atan2 )) );

//VECTOR_TYPE_OVERLOADS( true, 4, float, atan2 );
//VECTOR_TYPE_OVERLOADS( true, 4, double, atan2 );

// Hyperbolic-Sine
LO_PHALANX_PP_GENERIC( true )(
  sinh, 1,
  (( float, ( float ), ::sinhf ))
  (( double, ( double ), ::sinh )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, sinh );
VECTOR_TYPE_OVERLOADS( true, 4, double, sinh );

// Hyperbolic-Co-Sine
LO_PHALANX_PP_GENERIC( true )(
  cosh, 1,
  (( float, ( float ), ::coshf ))
  (( double, ( double ), ::cosh )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, cosh );
VECTOR_TYPE_OVERLOADS( true, 4, double, cosh );

// Hyperbolic-Tangent
LO_PHALANX_PP_GENERIC( true )(
  tanh, 1,
  (( float, ( float ), ::tanhf ))
  (( double, ( double ), ::tanh )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, tanh );
VECTOR_TYPE_OVERLOADS( true, 4, double, tanh );

// Arc-Hyperbolic-Sine
LO_PHALANX_PP_GENERIC( true )(
  asinh, 1,
  (( float, ( float ), ::asinhf ))
  (( double, ( double ), ::asinh )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, asinh );
VECTOR_TYPE_OVERLOADS( true, 4, double, asinh );

// Arc-Hyperbolic-Co-Sine
LO_PHALANX_PP_GENERIC( true )(
  acosh, 1,
  (( float, ( float ), ::acoshf ))
  (( double, ( double ), ::acosh )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, acosh );
VECTOR_TYPE_OVERLOADS( true, 4, double, acosh );

// Arc-Hyperbolic-Tangent
LO_PHALANX_PP_GENERIC( true )(
  atanh, 1,
  (( float, ( float ), ::atanhf ))
  (( double, ( double ), ::atanh )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, atanh );
VECTOR_TYPE_OVERLOADS( true, 4, double, atanh );

//
LO_PHALANX_PP_GENERIC( true )(
  pow, 2,
  (( float, ( float, float ), ::powf ))
  (( double, ( double, double ), ::pow )) );

//VECTOR_TYPE_OVERLOADS( true, 4, float, pow );
//VECTOR_TYPE_OVERLOADS( true, 4, double, pow );

LO_PHALANX_PP_GENERIC( false )(
  pow_fm, 2,
  (( float, ( float, float ), ::__powf )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, pow_fm );

//
LO_PHALANX_PP_GENERIC( false )(
  erf, 1,
  (( float, ( float ), ::erff ))
  (( double, ( double ), ::erf )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, erf );
VECTOR_TYPE_OVERLOADS( false, 4, double, erf );

//
LO_PHALANX_PP_GENERIC( false )(
  erfc, 1,
  (( float, ( float ), ::erfcf ))
  (( double, ( double ), ::erfc )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, erfc );
VECTOR_TYPE_OVERLOADS( false, 4, double, erfc );

//
LO_PHALANX_PP_GENERIC( false )(
  erfinv, 1,
  (( float, ( float ), ::erfinvf ))
  (( double, ( double ), ::erfinv )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, erfinv );
VECTOR_TYPE_OVERLOADS( false, 4, double, erfinv );

//
LO_PHALANX_PP_GENERIC( false )(
  erfcinv, 1,
  (( float, ( float ), ::erfcinvf ))
  (( double, ( double ), ::erfcinv )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, erfcinv );
VECTOR_TYPE_OVERLOADS( false, 4, double, erfcinv );

//
LO_PHALANX_PP_GENERIC( false )(
  lgamma, 1,
  (( float, ( float ), ::lgammaf ))
  (( double, ( double ), ::lgamma )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, lgamma );
VECTOR_TYPE_OVERLOADS( false, 4, double, lgamma );

//
LO_PHALANX_PP_GENERIC( false )(
  tgamma, 1,
  (( float, ( float ), ::tgammaf ))
  (( double, ( double ), ::tgamma )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, tgamma );
VECTOR_TYPE_OVERLOADS( false, 4, double, tgamma );

//
LO_PHALANX_PP_GENERIC( false )(
  fma, 3,
  (( float, ( float, float, float ), ::fmaf ))
  (( double, ( double, double, double ), ::fma )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, fma );
//VECTOR_TYPE_OVERLOADS( false, 4, double, fma );

//
LO_PHALANX_PP_GENERIC( false )(
  frexp, 2,
  (( float, ( float, int * ), ::frexpf ))
  (( double, ( double, int * ), ::frexp )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, frexp );
//VECTOR_TYPE_OVERLOADS( false, 4, double, frexp );

//
LO_PHALANX_PP_GENERIC( false )(
  ldexp, 2,
  (( float, ( float, int ), ::ldexpf ))
  (( double, ( double, int ), ::ldexp )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, ldexp );
//VECTOR_TYPE_OVERLOADS( false, 4, double, ldexp );

//
LO_PHALANX_PP_GENERIC( false )(
  scalbn, 2,
  (( float, ( float, int ), ::scalbnf ))
  (( double, ( double, int ), ::scalbn )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, scalbn );
//VECTOR_TYPE_OVERLOADS( false, 4, double, scalbn );

//
LO_PHALANX_PP_GENERIC( false )(
  scalbln, 2,
  (( float, ( float, long int ), ::scalblnf ))
  (( double, ( double, long int ), ::scalbln )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, scalbln );
//VECTOR_TYPE_OVERLOADS( false, 4, double, scalbln );

//
LO_PHALANX_PP_GENERIC( false )(
  logb, 1,
  (( float, ( float ), ::logbf ))
  (( double, ( double ), ::logb )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, logb );
VECTOR_TYPE_OVERLOADS( false, 4, double, logb );

//
LO_PHALANX_PP_GENERIC( false )(
  ilogb, 1,
  (( float, ( float ), ::ilogbf ))
  (( double, ( double ), ::ilogb )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, ilogb );
VECTOR_TYPE_OVERLOADS( false, 4, double, ilogb );

//
LO_PHALANX_PP_GENERIC( false )(
  fmod, 2,
  (( float, ( float, float ), ::fmodf ))
  (( double, ( double, double ), ::fmod )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, fmod );
//VECTOR_TYPE_OVERLOADS( false, 4, double, fmod );

//
LO_PHALANX_PP_GENERIC( false )(
  remainder, 2,
  (( float, ( float, float ), ::remainderf ))
  (( double, ( double, double ), ::remainder )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, remainder );
//VECTOR_TYPE_OVERLOADS( false, 4, double, remainder );

//
LO_PHALANX_PP_GENERIC( false )(
  remquo, 3,
  (( float, ( float, float, int * ), ::remquof ))
  (( double, ( double, double, int * ), ::remquo )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, sqrt );
//VECTOR_TYPE_OVERLOADS( false, 4, double, sqrt );

//
LO_PHALANX_PP_GENERIC( true )(
  modf, 2,
  (( float, ( float, float * ), ::modff ))
  (( double, ( double, double * ), ::modf )) );

//VECTOR_TYPE_OVERLOADS( true, 4, float, modf );
//VECTOR_TYPE_OVERLOADS( true, 4, double, modf );

//
LO_PHALANX_PP_GENERIC( false )(
  fdim, 2,
  (( float, ( float, float ), ::fdimf ))
  (( double, ( double, double ), ::fdim )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, fdim );
//VECTOR_TYPE_OVERLOADS( false, 4, double, fdim );

//
LO_PHALANX_PP_GENERIC( false )(
  trunc, 1,
  (( float, ( float ), ::truncf ))
  (( double, ( double ), ::trunc )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, trunc );
VECTOR_TYPE_OVERLOADS( false, 4, double, trunc );

//
LO_PHALANX_PP_GENERIC( true )(
  round, 1,
  (( float, ( float ), ::roundf ))
  (( double, ( double ), ::round )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, round );
VECTOR_TYPE_OVERLOADS( true, 4, double, round );

//
LO_PHALANX_PP_GENERIC( false )(
  rint, 1,
  (( float, ( float ), ::rintf ))
  (( double, ( double ), ::rint )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, rint );
VECTOR_TYPE_OVERLOADS( false, 4, double, rint );

//
LO_PHALANX_PP_GENERIC( false )(
  nearbyint, 1,
  (( float, ( float ), ::nearbyintf ))
  (( double, ( double ), ::nearbyint )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, nearbyint );
VECTOR_TYPE_OVERLOADS( false, 4, double, nearbyint );

//
LO_PHALANX_PP_GENERIC( true )(
  ceil, 1,
  (( float, ( float ), ::ceilf ))
  (( double, ( double ), ::ceil )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, ceil );
VECTOR_TYPE_OVERLOADS( true, 4, double, ceil );

//
LO_PHALANX_PP_GENERIC( true )(
  floor, 1,
  (( float, ( float ), ::floorf ))
  (( double, ( double ), ::floor )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, floor );
VECTOR_TYPE_OVERLOADS( true, 4, double, floor );

//
LO_PHALANX_PP_GENERIC( false )(
  lrint, 1,
  (( long int, ( float ), ::lrintf ))
  (( long int, ( double ), ::lrint )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, lrint );
//VECTOR_TYPE_OVERLOADS( false, 4, double, lrint );

//
LO_PHALANX_PP_GENERIC( false )(
  lround, 1,
  (( long int, ( float ), ::lroundf ))
  (( long int, ( double ), ::lround )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, lround );
//VECTOR_TYPE_OVERLOADS( false, 4, double, lround );

//
LO_PHALANX_PP_GENERIC( false )(
  llrint, 1,
  (( long long int, ( float ), ::llrintf ))
  (( long long int, ( double ), ::llrint )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, llrint );
//VECTOR_TYPE_OVERLOADS( false, 4, double, llrint );

//
LO_PHALANX_PP_GENERIC( false )(
  llround, 1,
  (( long long int, ( float ), ::llroundf ))
  (( long long int, ( double ), ::llround )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, llround );
//VECTOR_TYPE_OVERLOADS( false, 4, double, llround );

// signbit

// isinf

// isnan

// isfinite

//
LO_PHALANX_PP_GENERIC( false )(
  copysign, 2,
  (( float, ( float, float ), ::copysignf ))
  (( double, ( double, double ), ::copysign )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, copysign );
//VECTOR_TYPE_OVERLOADS( false, 4, double, copysign );

//
LO_PHALANX_PP_GENERIC( false )(
  fmin, 2,
  (( float, ( float, float ), ::fminf ))
  (( double, ( double, double ), ::fmin )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, fmin );
//VECTOR_TYPE_OVERLOADS( false, 4, double, fmin );

//
LO_PHALANX_PP_GENERIC( false )(
  fmax, 2,
  (( float, ( float, float ), ::fmaxf ))
  (( double, ( double, double ), ::fmax )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, fmax );
//VECTOR_TYPE_OVERLOADS( false, 4, double, fmax );

// Absolute number
LO_PHALANX_PP_GENERIC( true )(
  fabs, 1,
  (( float, ( float ), ::fabsf ))
  (( double, ( double ), ::fabs )) );

VECTOR_TYPE_OVERLOADS( true, 4, float, fabs );
VECTOR_TYPE_OVERLOADS( true, 4, double, fabs );

// Absolute number
LO_PHALANX_PP_GENERIC( true )(
  abs, 1,
  (( int, ( int ), ::abs ))
  (( long int, ( long int ), ::labs ))
  (( float, ( float ), ::fabsf ))
  (( double, ( double ), ::fabs )) );

VECTOR_TYPE_OVERLOADS( true, 4, int, abs );
VECTOR_TYPE_OVERLOADS( true, 4, long, abs );
VECTOR_TYPE_OVERLOADS( true, 4, float, abs );
VECTOR_TYPE_OVERLOADS( true, 4, double, abs );

// Absolute number (not supported in C90)
LO_PHALANX_PP_GENERIC( false )(
  abs, 1,
  (( long long int, ( long long int ), ::llabs )) );

VECTOR_TYPE_OVERLOADS( false, 4, longlong, abs );

//
struct nan
{
    const char *p_;

    PHALANX_CONFIG_DEFAULT_FUNCTION( false )
    explicit
    nan( const char *p_ )
      : p_( p_ ) {}

    PHALANX_CONFIG_DEFAULT_FUNCTION( false )
    operator double( void ) const
    { return ::nan( p_ ); }

    PHALANX_CONFIG_DEFAULT_FUNCTION( false )
    operator float( void ) const
    { return ::nanf( p_ ); }
};

//
LO_PHALANX_PP_GENERIC( false )(
  nextafter, 2,
  (( float, ( float, float ), ::nextafterf ))
  (( double, ( double, double ), ::nextafter )) );

//VECTOR_TYPE_OVERLOADS( false, 4, float, nextafter );
//VECTOR_TYPE_OVERLOADS( false, 4, double, nextafter );

//
LO_PHALANX_PP_GENERIC( false )(
  saturate, 1,
  (( float, ( float ), ::__saturatef )) );

VECTOR_TYPE_OVERLOADS( false, 4, float, saturate );


PHALANX_CONFIG_DEFAULT_FUNCTION( true )
float
real( float x_ )
{ return x_; }

PHALANX_CONFIG_DEFAULT_FUNCTION( true )
double
real( double x_ )
{ return x_; }

VECTOR_TYPE_OVERLOADS( true, 4, float, real );
VECTOR_TYPE_OVERLOADS( true, 4, double, real );

PHALANX_CONFIG_DEFAULT_FUNCTION( true )
float
imag( float )
{ return 0.0f; }

PHALANX_CONFIG_DEFAULT_FUNCTION( true )
double
imag( double )
{ return 0.0; }

VECTOR_TYPE_OVERLOADS( true, 4, float, imag );
VECTOR_TYPE_OVERLOADS( true, 4, double, imag );

PHALANX_CONFIG_DEFAULT_FUNCTION( true )
float
arg( float )
{ return 0.0f; }

PHALANX_CONFIG_DEFAULT_FUNCTION( true )
double
arg( double )
{ return 0.0; }

VECTOR_TYPE_OVERLOADS( true, 4, float, arg );
VECTOR_TYPE_OVERLOADS( true, 4, double, arg );

// TODO: implement this
PHALANX_CONFIG_DEFAULT_FUNCTION( false )
float
norm( float );

// TODO: implement this
PHALANX_CONFIG_DEFAULT_FUNCTION( false )
double
norm( double );

//VECTOR_TYPE_OVERLOADS( false, 4, float, norm );
//VECTOR_TYPE_OVERLOADS( false, 4, double, norm );

} // namespace math

} // namespace phalanx

#undef VECTOR_TYPE_OVERLOADS
#undef VECTOR_TYPE_OVERLOADS_DEF_
#undef EXPAND_
#undef EXPANDED_DECL

#endif // IG_PHALANX_MATH_FUNCTIONS_SPECIAL_FUNCTIONS_HPP_CU_ONCE_

